Skip to content

fix(cache): stop fsync-hammering the SD card on unchanged data#402

Open
ChuckBuilds wants to merge 1 commit into
mainfrom
fix/diskcache-sd-wear
Open

fix(cache): stop fsync-hammering the SD card on unchanged data#402
ChuckBuilds wants to merge 1 commit into
mainfrom
fix/diskcache-sd-wear

Conversation

@ChuckBuilds

@ChuckBuilds ChuckBuilds commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Summary

PR 1 of the performance/efficiency series (deep-dive findings). DiskCache.set did mkstemp → json.dump(indent=4) → flush+fsync → os.replace → chmod for every key write on the persistent /var/cache/ledmatrix — with sports plugins in live mode that's dozens to 100+ force-flushed SD-card writes per minute, mostly rewriting byte-identical API data each update cycle. fsync-per-write is the classic SD-card killer.

Changes (API unchanged):

  • Serialize once, compact — no indent=4 (machine-read files; indenting just multiplied bytes written).
  • Skip the disk entirely when the payload is unchanged for a key (adler32 digest map, per-process). The file's mtime is refreshed via os.utime so records that rely on mtime for TTL don't expire early; self-heals if the file was removed externally.
  • Drop the per-write fsync: os.replace already guarantees readers never see a torn file, and cache data is re-fetchable — the flush bought nothing but wear.

Worst case of the per-process digest map: another process rewrites data redundantly — never a missed write.

Verification

  • 6 new unit tests (skip keeps inode + refreshes mtime; changed data rewrites; clear resets; self-heal after external delete; compact JSON; DateTimeEncoder round-trip); full cache suite 44/44 green.
  • Devpi baseline measured before this series (10-min mmcblk0 diskstats): will post before/after deltas after deploy.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FqzC1nzTWL4kaqgMaQZFam

Summary by CodeRabbit

  • Performance
    • Avoided unnecessary disk rewrites when cached data remains unchanged.
    • Cache timestamps are refreshed efficiently while preserving the existing file.
  • Bug Fixes
    • Improved cache recovery when a cached file is deleted externally.
    • Ensured cleared entries can be written correctly again.
  • Data Handling
    • Persisted cache files use compact JSON formatting.
    • Date and time values continue to round-trip correctly through disk storage.

DiskCache.set wrote every key as mkstemp -> json.dump(indent=4) ->
flush+fsync -> replace -> chmod, on the persistent cache dir — dozens
of force-flushed SD writes per minute on an API-heavy install, mostly
rewriting identical data every plugin update cycle.

- Serialize once, compact (no indent): cache files are machine-read
  only; indenting multiplied the bytes written.
- Skip the disk when the payload for a key is unchanged (adler32 map,
  per-process); refresh the file mtime instead so records relying on
  mtime for TTL don't expire early. Self-heals if the file was removed
  externally (expiry cleanup).
- Drop the per-write fsync: os.replace already guarantees readers never
  see a torn file, and cache data is re-fetchable — the flush bought
  nothing but card wear.

API unchanged; DateTimeEncoder round-trip covered by tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FqzC1nzTWL4kaqgMaQZFam
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7bbf99d5-9e78-4e54-9c8b-509326347758

📥 Commits

Reviewing files that changed from the base of the PR and between 6edd80d and 3a55246.

📒 Files selected for processing (2)
  • src/cache/disk_cache.py
  • test/test_cache_manager.py

📝 Walkthrough

Walkthrough

DiskCache now serializes payloads once, tracks per-key Adler-32 digests, skips identical rewrites while refreshing mtimes, updates fallback writes, clears digest state, and adds tests for rewriting, recovery, compact JSON, and datetime persistence.

Changes

Disk cache write economy

Layer / File(s) Summary
Write economy and cache lifecycle
src/cache/disk_cache.py, test/test_cache_manager.py
DiskCache tracks serialized payload digests, avoids rewriting identical data, updates atomic and fallback write paths, resets digest state during clearing, and tests rewrite behavior, self-healing, compact JSON, and datetime round-tripping.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DiskCache
  participant DateTimeEncoder
  participant CacheFile
  DiskCache->>DateTimeEncoder: serialize payload
  DiskCache->>DiskCache: compute and compare Adler-32 digest
  DiskCache->>CacheFile: refresh mtime or write payload
  CacheFile-->>DiskCache: report write result
  DiskCache->>DiskCache: record or clear digest state
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: reducing unnecessary cache writes and fsync activity for unchanged data.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/diskcache-sd-wear

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 complexity · 0 duplication

Metric Results
Complexity 0
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant